{
  config,
  lib,
  pkgs,
  ...
}:

let

  cfg = config.services.home-manager.autoUpgrade;

  homeManagerPackage = config.programs.home-manager.package;

  autoUpgradeApp = pkgs.writeShellApplication {
    name = "home-manager-auto-upgrade";
    text =
      if cfg.useFlake then
        ''
          set -e
          if [[ ! -f "$FLAKE_DIR/flake.nix" ]]; then
              echo "No flake.nix found in $FLAKE_DIR." >&2
              exit 1
          fi
          echo "Changing to flake directory $FLAKE_DIR"
          cd "$FLAKE_DIR"
          echo "Update flake inputs"
          nix flake update
          echo "Upgrade Home Manager"
          home-manager switch --flake .
        ''
      else
        ''
          echo "Update Nix's channels"
          nix-channel --update
          echo "Upgrade Home Manager"
          home-manager switch
        '';
    runtimeInputs = with pkgs; [
      homeManagerPackage
      nix
    ];
  };
in
{
  meta.maintainers = [ lib.maintainers.pinage404 ];

  options = {
    services.home-manager.autoUpgrade = {
      enable = lib.mkEnableOption ''
        the Home Manager upgrade service that periodically updates your Nix
        channels before running `home-manager switch`'';

      frequency = lib.mkOption {
        type = lib.types.str;
        example = "weekly";
        description = ''
          The interval at which the Home Manager auto upgrade is run.
          This value is passed to the systemd timer configuration
          as the `OnCalendar` option.
          The format is described in
          {manpage}`systemd.time(7)`.
        '';
      };

      useFlake = lib.mkOption {
        type = lib.types.bool;
        default = false;
        description = "Whether to use 'nix flake update' instead of 'nix-channel --update'.";
      };

      flakeDir = lib.mkOption {
        type = lib.types.str;
        default = "${config.xdg.configHome}/home-manager";
        defaultText = lib.literalExpression ''"''${config.xdg.configHome}/home-manager"'';
        example = "/home/user/dotfiles";
        description = "The directory of the flake to update.";
      };
    };
  };

  config = lib.mkIf cfg.enable {
    assertions = [
      (lib.hm.assertions.assertPlatform "services.home-manager.autoUpgrade" pkgs lib.platforms.linux)
    ];

    systemd.user = {
      timers.home-manager-auto-upgrade = {
        Unit.Description = "Home Manager upgrade timer";

        Install.WantedBy = [ "timers.target" ];

        Timer = {
          OnCalendar = cfg.frequency;
          Unit = "home-manager-auto-upgrade.service";
          Persistent = true;
        };
      };

      services.home-manager-auto-upgrade = {
        Unit.Description = "Home Manager upgrade";
        Service = {
          ExecStart = "${autoUpgradeApp}/bin/home-manager-auto-upgrade";
          Environment = lib.mkIf cfg.useFlake [ "FLAKE_DIR=${cfg.flakeDir}" ];
        };
      };
    };
  };
}
